<?php

class Model{
	
	public $name;
	public $table;
	public $table_fields = array();
	private $_connection;
	private $_queryModelFieldDelimiter = "!";
	
	public $relations = array();
	
	public function __construct() {
		global $CONFIG;
		$db = $CONFIG["DATABASE"];
		$this->_connection = mysql_connect($db["host"].($db["port"]?":".$db["port"]:""), $db["username"], $db["password"]) or die("Error connecting to Database Server. Please verify your database connection configuration.");
		mysql_select_db($db["database"], $this->_connection) or die("Error selecting Database. Please verify configuration");
		if($db["encoding"]) {
			mysql_query("SET NAMES ".$db["encoding"]);
		}
		
		if(!$this->name) {
			$this->name = preg_replace('/Model$/', "", get_class($this));
		}
		if($this->name) {
			if(!$this->table){
				$this->table = self::getTableName($this->name);
			}
		}
		
		// load fields
		$res = $this->_query_sql("DESCRIBE `{$this->table}`");
		$this->table_fields = array();
		foreach ($res as $row) {
			$this->table_fields[] = $row[$this->name];
		}
	}
	
	private static $_instance;
	private static function _getInstance(){
		if(!self::$_instance) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	public static function getTableName($model){
		return pluralize(uncamelize($model));
	}
	public static function getModelName($table){
		return singularize(camelize($table));
	}
	private function _getFieldsLabels($fields){
		$fields = is_array($fields) ? $fields : (array)$fields;
		$ret = "";
		foreach($fields as $def) {
			$model = "{$this->name}";
			$objectname = "{$model}Model";
			$Model = new $objectname;
			$fields = "$def";
			if(preg_match('/\./', $def)){
				list($model, $fields) = explode(".", $def, 2);
				$model = preg_replace('/`/', "", $model);
				$objectname = "{$model}Model";
				$Model = new $objectname;
			}
			$fields = empty($fields) ? "*" : $fields;
			
			if(preg_match('/^`?\*`?$/', $fields)) {
				foreach ($Model->table_fields as $field) {
					$ret .= "`{$model}`.`".$field["Field"]."` as `{$model}{$this->_queryModelFieldDelimiter}".$field["Field"]."`, ";
				}
			}else{
				$fields = preg_replace('/`/', "", $fields);
				$ret .= "`{$model}`.`$fields` as `{$model}{$this->_queryModelFieldDelimiter}{$fields}`, ";
			}
		}
		return preg_replace('/\,\s*$/', "", $ret);
	}
	
	private function _buildQuery($options){
		// SELECT
		$q_select = "SELECT ";
		$select_fields = $options["select"];
		if(!$select_fields) {
			$select_fields = "`{$this->name}`.*";
		}
		$q_select .= $this->_getFieldsLabels($select_fields);
		
		// FROM
		$q_from = "FROM `{$this->table}` as `{$this->name}`";
		foreach($this->relations as $type=>$data) {
			switch ($type) {
				case "many_many":
					foreach ($data as $model=>$definition) {
						if(preg_match('/^\d+$/', $model)){
							$model = $definition;
							$definition = array();
						}
						
						$tablename = self::getTableName($model);
						$jointablename = "{$this->table}_{$tablename}";
						$joinModelName = $this->name.self::getModelName($tablename);
						
						if(!$options["select"]) {
							$q_select .= ", ".$this->_getFieldsLabels("`{$model}`.*");
						}
						$q_from .= " ".$definition["join_type"] . " JOIN `$jointablename` as `{$joinModelName}` ON `{$this->name}`.`id`=`{$joinModelName}`.`".uncamelize($this->name)."_id` ";
						$q_from .= " ".$definition["join_type"] . " JOIN `$tablename` as `{$model}` ON `{$model}`.`id`=`{$joinModelName}`.`".uncamelize($model)."_id` ";
					}
					break;
			}
		}
		
		// WHERE
		$q_where = "WHERE 1";
		if($options["filter"]) {
			foreach($options["filter"] as $k=>$v) {
				if(in_array(strtoupper($k), array("AND", "OR"))) {
					$q_where .= "$k (";
					foreach ($v as $m=>$n) {
						$q_where .= " AND $m = '$n'";
					}
					$q_where = preg_replace('/\s*and$|\s*or$/', "", $q_where) . ")";
				}else{
					$q_where .= " AND $k = '$v'";
				}
			}
		}
		$q_group = $options["group"] ? "GROUP BY ".$options["group"] : "";
		$q_having = $options["having"] ? "HAVING ".$options["having"] : "";
		$q_limit = $options["limit"] ? 	"LIMIT ".$options["limit"] : "";
		
		$query = trim("$q_select $q_from $q_where $q_group $q_having $q_limit");
		return $query;
	}
	
	private function _query($options){
		$query = $this->_buildQuery($options);
		return $this->_query_sql($query);
	}
	private function _query_sql($sql){
		if($res = mysql_query($sql)) {
			if(mysql_num_rows($res)) {
				$ret = array();
				while($r = mysql_fetch_assoc($res)){
					$row = array();
					foreach ($r as $k=>$v) {
						if(preg_match('/\!/', $k)){
							list($model,$field) = explode($this->_queryModelFieldDelimiter, $k, 2);
						}else{
							$model = $this->name;
							$field = $k;
						}
						$row[$model][$field] = $v;
					}
					$ret[] = $row;
				}
			}else{
				$ret = NULL;
			}
			return $ret;
		}else{
			$this->errno = mysql_errno($this->_connection);
			$this->error = mysql_error($this->_connection) . "\n\n<br />\n\n$sql";
			return false;
		}
	}
	
	public function find($options){
		$res = $this->_query($options);
		if($res===false) {
			die($this->error);
		}else{
			return $res;
		}
	}
	public function findFirst($options) {
		$options["limit"] = 1;
		$row = $this->find($options);
		if($row) {
			$row = $row[0];
		}
		return $row;
	}
}